import { TransferNode, TransferTree } from "algorithm/TransferTree/TransferTree";
 
import { EntityCompent } from "../../components/EntityCompent";
import { PositionHelper } from "utils/position/PositionHelper";
import { CreepNode } from "algorithm/TransferTree/CreepContext";
import { cache_return } from "utils/decorate/decorate";
import { IEntity } from "../../Entity.interface";
import { ABaseEntity } from "../../BaseEntity";

abstract class  IWorkEntity<U extends INoMemory =INoMemory> extends ABaseEntity<U>
{
   abstract Controller:StructureController;
}
/**
 * 升级组件
*/
export   class UpgradeComponent< T extends IWorkEntity >  extends EntityCompent<T>
{
    private _placePosition!:RoomPosition[]  // 0 container|storage 1 manager 2.link|terminal
    private _transferTree!:TransferTree;
    private _controller_ram_pos :RoomPosition[]=[];    // 控制器周围的ram
    private workers!:Creep[];   // 升级工
    _isStructureRoot:boolean=true;// 是否是建筑root 取能量的地方
    getRoot!:(key:IRoomPosition)=>AnyStoreStructure|undefined
    getRootCreep!:(key:IRoomPosition)=>Creep|undefined
    rootStore?:AnyStoreStructure;
    private posfilter!:(pos:IRoomPosition)=>boolean
    public initTransferTree(placePosition:RoomPosition[],filter:(pos:IRoomPosition)=>boolean)
    {
        this._placePosition = placePosition;
        this._transferTree = new TransferTree();
        // ,this._placePosition[2]
        this._transferTree.init([this._placePosition[0]],this.Entity.Controller.pos,3,filter);
        this.rootStore = PositionHelper.lookFor(LOOK_STRUCTURES,this._placePosition[0]).filter(e=>e.structureType == STRUCTURE_CONTAINER || e.structureType == STRUCTURE_STORAGE || e.structureType == STRUCTURE_TERMINAL)[0] as AnyStoreStructure;
        this.posfilter = filter;
    }
    public updateEnergyTree(room:Room)
    {
        // 重新构建能量的传输树?有没有必要
        // this.rootStore = this.getRoot(this._placePosition[0]);
        if(this.rootStore)
        {
            this.rootStore = Game.getObjectById(this.rootStore.id)!;
            if(this.rootStore.store.getUsedCapacity(RESOURCE_ENERGY)==0)
            {
                // 当前的root没有能量，则尝试更换
                const containers = room.controller!.getContainer();
                const stores:(AnyStoreStructure|undefined)[] = [room.storage,room.terminal].concat(containers as any[]);
                for(const e of stores)
                {
                    if(e && e.store.getUsedCapacity(RESOURCE_ENERGY)>0 && e.pos.distance(room.controller!.pos)<=4)
                    {
                        if(this.rootStore.id != e.id)
                        {
                            this.rootStore = e;
                            this._placePosition = [e.pos] ;
                            this._transferTree.init([e.pos],this.Entity.Controller.pos,3,  this.posfilter);
                            this.Entity.log("warn",`重新构建升级树`,e.structureType);
                            return ;
                        }
                    }
                }
            }
            // this.Entity.log("info",'仓库',this.rootStore.structureType,`${this.rootStore.store.getUsedCapacity(RESOURCE_ENERGY)}`);
        }
        else 
        {
            this.Entity.log("warn",`没有根store`);
        }
    }

    public dowork(workers:Creep[])
    {
        
        // this.Entity.log(`info`,`  ${_.map(workers,e=>e.name)} exec`);
        this.balanceUpgrader(workers);
        this.balanceEnergy( )
    }

    public setRampartPos(poss:RoomPosition[])
    {
        this._controller_ram_pos = poss;
    }

    private get controller()
    {
        return this.Entity.Controller;
    }
    private get controllerPos()
    {
        return this.controller.pos;
    }
    public get TransferTree()
    {
        return this._transferTree
    }
    /**
     * 调整升级工的位置
     * @param upgraders 
     */
    public balanceUpgrader(upgraders:Creep[])
    {
        if(!this._placePosition[0])
        {
            return 
        }
        this.workers = upgraders;
        const creeps:Record<number,Creep>=this.getUpgraderRecord(upgraders);

        // 1. 设置在树内的爬的移动
        const rootnode:Record<number,IRoomPosition>={};
        if(this._placePosition[0])
        {
            rootnode[PositionHelper.to1D(this._placePosition[0])] = this._placePosition[0];
        }

        const testconext = new CreepNode<IRoomPosition>(creeps,rootnode);
        let result = this.TransferTree.balance(testconext);
        // this.TransferTree.print();
        for(const e of result.move)
        {
            const key =PositionHelper.to1D(e.From);
            if(creeps[key])
            {
                creeps[key].move(PositionHelper.getDirection(e.From,e.To));
            }
            else
            {
                // global.Log.Warning(`${key}  找不到爬 ${JSON.stringify(e.From)}`);
            }
            
        }

        // 2.设置不在树内的移动目标
        this.dispatchMoveTask(creeps,result.moveto)
    }
 
    private dispatchMoveTask(creeps:Record<number,Creep>,moveto:INodeActionMoveTo<IRoomPosition>[])
    {

        const existgoel:Record<number,INodeActionMoveTo<IRoomPosition>>={};

        for(const e of moveto)
        {
            existgoel[PositionHelper.to1D(e.To)]=e;
        }

        for(const key in creeps)
        {
            if(this.TransferTree.getWorkSlot(creeps[key].pos))
            {
                // global.Log.Info(`升级爬 在位置上 ${JSON.stringify(creeps[key].getPos())}`);
                
                continue;
            }


            const leftgoals = Object.keys(existgoel);
            if(leftgoals.length==0)
            {
                return ;
            }
            // 优先移动到距离控制器最近的叶子节点
            const  move_goal = _.min(existgoel,e=>PositionHelper.Distance(e.To,this.controllerPos));
            const goal = move_goal;
            creeps[key].mymoveTo({pos:PositionHelper.Create( goal.To),range:0},{});
        }
    }
 
    private doWork(creep:Creep,controller:StructureController= this.controller)
    {
        const sites = this._placePosition[2]?.lookFor(LOOK_CONSTRUCTION_SITES);
        if(sites && sites.length>0)
        {
            creep.build(sites[0])
            return ;
        }
        const createrampart = (pos:RoomPosition)=>{

            const ret = pos.lookFor(LOOK_STRUCTURES).filter(e=>e.structureType== STRUCTURE_RAMPART);
            if(ret.length>0)
            {
                if(ret[0].hits<ret[0].hitsMax*0.1 && ret[0].hits<1000000)
                {
                    creep.repair(ret[0]);
                    return true;
                }

            }
            else
            {
                const ret = pos.lookFor(LOOK_CONSTRUCTION_SITES).filter(e=>e.structureType== STRUCTURE_RAMPART);
                if(ret.length>0)
                {
                    creep.build(ret[0]);
                    return true;
                }
                if(OK == pos.createConstructionSite(STRUCTURE_RAMPART))
                {
                    //return false
                }

            }
            return false;
        }

        for(const e of this._controller_ram_pos)
        {
            if(createrampart( e))
            {
                return
            }
        }
        creep.upgradeController(controller);
    }
 
    /**
     * 平衡爬之间的能量 且执行工作
     * @param resorceType 
     * @param worker 
     */
    private balanceEnergy(resorceType:ResourceConstant=RESOURCE_ENERGY,worker = this.doWork.bind(this))
    {
        const creeps:Record<number,Creep>=this.getUpgraderRecord(this.workers);
        for(const e of this.workers)
        {
            
            worker(e,this.controller);
            const work_count  = e.getActiveBodyparts(WORK);

            const workslot = this.TransferTree.getWorkSlot(e.pos);
            if(!workslot )
            {
                
            continue;
            }
            if(this.backResource(workslot,creeps))
            {
            continue;
            }
            if(e.store.getUsedCapacity(resorceType) >work_count*2 )
            {
                continue;
            }
            if(!workslot.parentNode)
            {
                continue;
            }
            if(workslot.parentNode.parentNode)
            {
                // 上一级不是根节点，爬之间查询
                const parentcreep = creeps[workslot.parentNode.posKey];
                const thiscreep = creeps[workslot.posKey];
                if(!parentcreep || !thiscreep)
                {
                    continue;
                }
                if(thiscreep.store.getUsedCapacity(resorceType)>0 &&thiscreep.ticksToLive&&thiscreep.ticksToLive<3)
                {
                    thiscreep.transfer(parentcreep,resorceType);
                    continue;
                }
                let space = thiscreep.store.getFreeCapacity(resorceType);
                let has = Math.max(parentcreep.store.getUsedCapacity(resorceType)-parentcreep.getActiveBodyparts(WORK),0);
                let cap1 = thiscreep.store.getCapacity();
                let cap2 = parentcreep.store.getCapacity();
                // if(Math.min(space,has)>cap1/2 ||Math.min(space,has)>cap2/2 )
                {
                    parentcreep.transfer(thiscreep,resorceType,Math.min(space,has));
                }

            }
            else
            {
                // 上一级是根节点
                this.getFromRoot(workslot as Required<TransferNode<IRoomPosition>>,creeps,resorceType);
            }
        }
    }
 
    private backResource( workslot:TransferNode<IRoomPosition>,creeps:Record<number,Creep>,resorceType= RESOURCE_ENERGY)
    {
        if(!workslot.parentNode)
        {
            return false;
        }
        const thiscreep = creeps[workslot.posKey];
        if(thiscreep&&  thiscreep.ticksToLive&&thiscreep.ticksToLive<3)
        {
            if(workslot.parentNode.parentNode)
            {
                const parentcreep = creeps[workslot.parentNode.posKey];
                if(parentcreep)
                {
                    thiscreep.transfer(parentcreep,resorceType);
                    return true;
                }

            }
            else
            {
                if(this._isStructureRoot)
                {
                    const anystore = this.getRoot(workslot.parentNode.pos);
                    if(thiscreep.store.getUsedCapacity(resorceType)>0 && anystore)
                    {
                        thiscreep.transfer(anystore,resorceType);
                        return true;
                    }
                }
                else  
                {
                    const anystore = this.getRootCreep(workslot.parentNode.pos);
                    if(thiscreep.store.getUsedCapacity(resorceType)>0 && anystore)
                    {
                        thiscreep.transfer(anystore,resorceType);
                        return true;
                    }
                }
            }
           
        }
        return false;
    }
 
 
    private getFromRoot(workslot:Required<TransferNode<IRoomPosition>>,creeps:Record<number,Creep>,resorceType:ResourceConstant= RESOURCE_ENERGY)
    {
        if(this._isStructureRoot)
        {
            this.getFromRootOfAnyStore(workslot,creeps,resorceType);
        }
        else
        {
            this.getFromRootOfCreep(workslot,creeps,resorceType);
        }
    }

    // 从建筑获取资源
    private getFromRootOfAnyStore(workslot:Required<TransferNode<IRoomPosition>>,creeps:Record<number,Creep>,resorceType:ResourceConstant= RESOURCE_ENERGY)
    {
        let anystore = this.getRoot(workslot.parentNode.pos);
        const thiscreep = creeps[workslot.posKey];
        if( !thiscreep)
        {
            this.Entity.log("error",`${JSON.stringify(Object.keys(creeps))} can not find creep ${workslot.posKey}`);
            return
        }
      
        
        if( anystore )
        {
            let space = thiscreep.store.getFreeCapacity(resorceType);
            let has = anystore.store.getUsedCapacity(resorceType) as number;
            
            if(Math.min(space,has)>0)
            {
                thiscreep.withdraw(anystore,resorceType);
                return 
            }
        } 
        
        
    }
 
     // 从爬获取资源
    private getFromRootOfCreep(workslot:Required<TransferNode<IRoomPosition>>,creeps:Record<number,Creep>,resorceType:ResourceConstant= RESOURCE_ENERGY)
    {
        const rootStore = this.getRootCreep(workslot.parentNode.pos);
        const thiscreep = creeps[workslot.posKey];
        if(!rootStore || !thiscreep)
        {
            return
        }
        let space = thiscreep.store.getFreeCapacity(resorceType);
        let has = rootStore.store.getUsedCapacity(resorceType);
        if(Math.min(space,has)>0)
        {
            (rootStore as Creep).transfer(thiscreep,resorceType,Math.min(space,has));
        }
    }

     @cache_return("cache",0)
    private getUpgraderRecord(upgrades:Creep[])
    {
        const creeps:Record<number,Creep>={};
        for(const e of upgrades)
        {
            creeps[PositionHelper.to1D(e.pos)]=e;
        }
        return creeps;
    }
 
}
